package cn.bytengine.stem.cluster.netty;

import cn.bytengine.stem.contexts.Context;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.PooledByteBufAllocator;
import io.netty.channel.*;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;

public class ClusterHostNettyTcpServer extends NettyServer<ServerBootstrap, ServerChannel> {

    public ClusterHostNettyTcpServer(Context config) {
        super(config);
    }

    // region internal methods

    @Override
    protected ServerBootstrap bootstrap(Context context, int port, ChannelInitializer<? extends Channel> channelInitializer) {
        return new ServerBootstrap()
                .group(getBossLoopGroup(), getWorkerLoopGroup())
                .channel(NioServerSocketChannel.class)
                .option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT)
                .option(ChannelOption.SO_BACKLOG, context.getOrDefaultInteger("netty.tcp.server.backlog", 1024))
                .option(ChannelOption.AUTO_CLOSE, context.getOrDefaultBoolean("netty.tcp.server.auto_close", true))
                .option(ChannelOption.SO_REUSEADDR, context.getOrDefaultBoolean("netty.tcp.server.reuseaddr", true))
                .childOption(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT)
                .childOption(ChannelOption.SO_KEEPALIVE, context.getOrDefaultBoolean("netty.tcp.server.keepalive", true))
                .childOption(ChannelOption.TCP_NODELAY, context.getOrDefaultBoolean("netty.tcp.server.nodelay", true))
                .childHandler(channelInitializer);
    }

    // endregion

    // region implemented methods
    @Override
    public void startup(Context context) throws Exception {
        Integer port = context.getInteger("netty.tcp.server.port");
        if (port == null || port < 1) {
            throw new IllegalArgumentException("The 'netty.tcp.server.port' value is null or less then the 1.");
        }
        ScoketChannelInitializerSupplier supplier = context.getByType("netty.tcp.server.pipeline_factory", ScoketChannelInitializerSupplier.class);
        if (supplier == null) {
            throw new IllegalArgumentException("NettyTcpClusterHostServer startup context not contain the called 'netty.tcp.server.pipeline_factory' ChannelInitializerSupplier instance.");
        }
        ChannelInitializer<SocketChannel> channelInitializer = supplier.get();
        if (channelInitializer == null) {
            throw new IllegalArgumentException("The called 'netty.tcp.server.pipeline_factory' ChannelInitializerSupplier result is null in NettyTcpClusterHostServer startup context.");
        }

        ServerBootstrap bootstrap = bootstrap(context, port, channelInitializer);

        try {
            ChannelFuture channelFuture = bootstrap.bind(port).sync();
            getChannelGroup().add(channelFuture.channel());
        } catch (Exception e) {
            shutdown();
            throw e;
        }
    }
}
